#pragma once
#include <cassert>
#include <cstring>
#include <unistd.h>
#include <vector>
#include "Debug.h"

namespace xncc {
namespace foundation {
    class Buffer
    {
      public:
        explicit Buffer(size_t initialSize = 1024) : buffer_(initialSize)
        {
            debug_assert(readableBytes() == 0, "initial size should be 0");
            debug_assert(writableBytes() == initialSize, "initial size should be equal to buffer size:", initialSize);
#
        }
        [[nodiscard]] size_t readableBytes() const { return writerIndex_ - readerIndex_; }
        [[nodiscard]] size_t writableBytes() const { return buffer_.size() - writerIndex_; }

        [[nodiscard]] const char* peek() const { return begin() + readerIndex_; }

        void retrieve(size_t len)
        {
            debug_assert(len <= readableBytes(), "readableBytes should be greater than ", len);
            if (len < readableBytes()) {
                readerIndex_ += len;
            }
            else {
                retrieveAll();
            }
        }

        void append(const char* /*restrict*/ data, size_t len)
        {
            ensureWritableBytes(len);
            std::copy(data, data + len, beginWrite());
            hasWritten(len);
        }

        ssize_t readFd(int fd, int* savedErrno);

      private:
        void ensureWritableBytes(size_t len)
        {
            if (writableBytes() < len) {
                makeSpace(len);
            }
            debug_assert(writableBytes() >= len, "writableBytes should be greater than ", len);
        }
        void hasWritten(size_t len) { writerIndex_ += len; }

        void retrieveAll()
        {
            readerIndex_ = 0;
            writerIndex_ = 0;
        }
        [[nodiscard]] size_t prependableBytes() const { return readerIndex_; }

        char* begin() { return &*buffer_.begin(); }

        [[nodiscard]] const char* begin() const { return &*buffer_.begin(); }
        char*                     beginWrite() { return begin() + writerIndex_; }
        [[nodiscard]] const char* beginWrite() const { return begin() + writerIndex_; }
        void                      makeSpace(size_t len)
        {
            if (writableBytes() + prependableBytes() < len) {
                buffer_.resize(writerIndex_ + len);
            }
            else {
                // move readable data to the front, make space inside buffer
                size_t readable = readableBytes();
                std::copy(begin() + readerIndex_, begin() + writerIndex_, begin());
                readerIndex_ = 0;
                writerIndex_ = readerIndex_ + readable;
            }
        }

        std::vector<char> buffer_;
        size_t            readerIndex_ = 0;
        size_t            writerIndex_ = 0;
    };
}  // namespace foundation
}  // namespace xncc
